package diversion.v1.network;

//~--- non-JDK imports --------------------------------------------------------

import diversion.v1.security.KeyGen;
import diversion.v1.util.Base64;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetAddress;

import java.net.ServerSocket;
import java.net.Socket;

import java.security.KeyPair;
import java.util.HashMap;
import java.util.Iterator;
import javax.crypto.interfaces.DHPrivateKey;
import javax.crypto.interfaces.DHPublicKey;

/**
 *  Description of the Class
 *
 * @author    jmricker
 */
public class Server {
    
    private Dispatcher             dispatch;
    private transient DHPrivateKey privateKey;
    private DHPublicKey            publicKey;
    private String                 publicKeyString;
    private HashMap openPorts;
    
    /**
     * Creates a new multi-threaded Server object that can listen to multiple
     * ports for incoming connections.
     *
     * @param  serverKeys  pair of private and public encryption keys
     */
    public Server(KeyPair serverKeys) {
        this.publicKey       = (DHPublicKey) serverKeys.getPublic();
        this.privateKey      = (DHPrivateKey) serverKeys.getPrivate();
        this.publicKeyString = Base64.encodeBytes(this.publicKey.getEncoded());
        this.dispatch        = new ServerDispatcher();
        this.openPorts = new HashMap();
        
    }
    
    /**
     *  Description of the Method
     *
     * @param  port  Description of the Parameter
     */
    public void open(final int port) {
        
        final Server parent = this;
        
        Thread       t   = new Thread() {
            
            public void run() {

                try {
                    Socket       incomingSocket;
                    ServerSocket listener = new ServerSocket(4444, 1, InetAddress.getByName("127.0.0.1"));
                    System.out.println(InetAddress.getLocalHost());
                    parent.openPorts.put(new Integer(port), listener);                   
                    System.out.println(parent);
                    while (true) {
                        doComms connection;

                        incomingSocket = listener.accept();
                        System.out.println(incomingSocket);

                        doComms conn_c = new doComms(incomingSocket, parent.dispatch);
                        Thread  t      = new Thread(conn_c);

                        t.start();
                    }
                } catch (IOException ioe) {
                    System.out.println("IOException on port listen: " + ioe);
                    ioe.printStackTrace();
                }
            }
        };

        t.start();
    }
    
    public void close(int port) {
        
        ServerSocket listener = (ServerSocket) this.openPorts.get(new Integer(port));
        try {
            listener.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        
    }
    
    public String toString() {
        
        String total = "";
       
        for (Iterator it = this.openPorts.keySet().iterator(); it.hasNext();) {            
            ServerSocket server = (ServerSocket) this.openPorts.get(it.next());            
            total += server.toString() + ", " + server.getLocalSocketAddress().toString();
        }
        
        return total;
        
    }

    /**
     *  The main program for the Server class
     *
     * @param  args  The command line arguments
     */
    public static void main(String[] args) {
        KeyPair keys = null;

        try {
            keys = KeyGen.loadKeys("server.key");
        } catch (IOException ie) {
            System.out.println("Server Keys not found.");
        }

        if (keys == null) {
            try {
                keys = KeyGen.generateKeys();
            } catch (java.security.GeneralSecurityException e) {
                System.out.println("Fatal Error: " + e);

                return;
            }

            KeyGen.saveKeys("server.key", keys);
        }

        int port = 0;
        try {
            
            port = Integer.parseInt(args[1]);
        
        } catch (NumberFormatException ex) {
            port = 4444;
            
        }
        
        Server s = new Server(keys);        
        s.open(port);        
        System.out.println(s);
        
    }

}